export class Typewriter {
  private queue: string[] = []
  private consuming = false
  private timmer: any
  constructor(private onConsume: (str: string) => void) {
  }

  dynamicSpeed() {
    //2 秒中打印完队列中的所有字符, 如果只有几个字符, 那么 speed > 200, 此时延时时间直接返回 200
    //防止停顿时间过长才打印几个字符
    const speed = 2000 / this.queue.length
    if (speed > 200) {
      return 200
    } else {
      return speed
    }
  }

  //add 方法将 str 内容拆成很多单个字符添加到 queue 队列中
  add(str: string) {
    if (!str) return
    // 使用正则表达式保留换行符
    const chars = str.match(/\n|.|\r/g) || []
    this.queue.push(...chars)
  }

  consume = () => {
    //队列中全是单个字符, 如果队列有数据才会消费
    if (this.queue.length > 0) {
      const str = this.queue.shift()
      //取出单个字符消费
      str && this.onConsume(str)
    }
  }

  /**
   这是一个递归的定时器实现：

    1. setTimeout 创建一个定时器，延迟一段时间后执行回调函数
    2. 延迟时间由 dynamicSpeed() 动态计算，根据队列长度调整显示速度
    3. 在回调函数中：
      - 先消费一个字符（ this.consume() ）
      - 如果 consuming 为 true（表示还在打字过程中）
      - 则调用 this.next() 继续下一轮定时器
    4. 这样就形成了循环：显示字符 -> 等待时间 -> 显示下一个字符 -> 等待时间...
    通过这种方式实现了文字一个接一个显示的打字机效果。
    当 consuming 设为 false 或队列为空时，这个循环就会停止。
  */
  next = () => {
    this.consume()
    //定时
    this.timmer = setTimeout(() => {
      this.consume() // 消费一个字符
      if (this.consuming) { // 如果还在消费状态
        this.next()  // 继续下一个循环
      }
    }, this.dynamicSpeed()) // 动态计算延迟时间
  }

  start = () => {
    this.consuming = true
    this.next()
  }
  done = () => {
    this.consuming = false
    clearTimeout(this.timmer)
    this.onConsume(this.queue.join(''))
    this.queue = []
  }
}

const parsePack = (str: string) => {
  const pattern = /data:\s*({.*?})\s*\n/g
  const result = []
  let match
  while ((match = pattern.exec(str)) !== null) {
    const jsonStr = match[1]
    try {
      const json = JSON.parse(jsonStr)
      result.push(json)
    } catch (e) {
      console.log(e)
    }
  }
  return result
}

export interface GptMsg {
  role: string
  content: string
}
export type GptMsgs = Array<GptMsg>
export class StreamGpt {
  onStart: (prompt: string) => void
  onCreated: () => void
  onDone: () => void
  onPatch: (text: string) => void
  constructor(private key: string, options: {
    onStart: (prompt: string) => void
    onCreated: () => void
    onDone: () => void
    onPatch: (text: string) => void
  }) {
    const { onStart, onCreated, onDone, onPatch } = options
    this.onStart = onStart
    this.onCreated = onCreated
    this.onPatch = onPatch
    this.onDone = onDone
  }
  async stream(prompt: string, history: GptMsgs = [], preset: GptMsgs = []) {
    let finish = false
    let count = 0
    const _history = [...history]
    this.onStart(prompt)
    const res = await this.fetch([...preset, ..._history, { 'role': 'user', content: prompt }])
    if (!res.body) return
    const reader = res.body.getReader()
    const decoder: TextDecoder = new TextDecoder()
    while (!finish) {
      const { done, value } = await reader.read()
      if (done) {
        finish = true
        this.onDone()
        break
      }
      count++
      const jsonArray = parsePack(decoder.decode(value))
      if (count === 1) {
        this.onCreated()
      }
      jsonArray.forEach((json: any) => {
        if (!json.content) {
          return
        }
        console.log('Received content:', JSON.stringify(json.content)) // 检查内容是否包含换行符
        this.onPatch(json.content)
      })
    }
  }
  // async fetch(messages: GptMsgs) {
  //   return await fetch('https://api.openai.com/v1/chat/completions', {
  //     method: 'POST',
  //     body: JSON.stringify({
  //       model: 'gpt-3.5-turbo',
  //       messages,
  //       stream: true
  //     }),
  //     headers: {
  //       'Content-Type': 'application/json',
  //       Authorization: `Bearer ${this.key}`
  //     }
  //   })
  // }

  async fetch(messages: GptMsgs) {
    //http://localhost:3000/v1/chat/completions
    //http://10.118.18.63:3000/v1/chat/completions
    // 获取完整的请求配置
    const config = (window as any).Android?.getRequestConfig('chat') || {
      url: 'http://localhost:3000/api/markdown',
      method: 'POST',
      body: {
        model: 'gpt-3.5-turbo',
        messages,
        stream: true
      },
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${this.key}`
      }
    };

    return await fetch(config.url, {
      method: config.method,
      body: JSON.stringify(config.body),
      headers: config.headers
    });
  }
}
